Compose 使用 Kotlin 語言的宣告式語法,讓開發者能夠以更直觀、更簡潔的方式來構建使用者介面。
    Kotlin 協同程式,也是很重要的概念。
提供了一種非同步且輕量級的方式來處理長時間運行的任務,例如網路請求、資料庫操作等,而不阻塞主執行緒。
在主程式直接delay
import kotlinx.coroutines.*
fun main() {
   println("iThome鐵人賽")
   delay(1000)
   println("30天")
}
就會噴 error ,就鐵人賽delay 1天,就斷賽一樣
阻塞主執行緒,等待協同程式完成
import kotlinx.coroutines.*
fun main() {
    runBlocking {
        println("iThome鐵人賽")
        delay(1000)
        println("30天")
    }
}
iThome鐵人賽
30天
import kotlinx.coroutines.*
fun main() {
    runBlocking {
        println("iThome鐵人賽")
        printCompetitionDays()
    }
}
suspend fun printCompetitionDays() {
    delay(1000)
    println("30天")
}
iThome鐵人賽
30天
import kotlin.system.*
import kotlinx.coroutines.*
fun main() {
    val time = measureTimeMillis {
        runBlocking {
            println("iThome鐵人賽")
            printCompetitionDays()
            printCompetitionCategory()
        }
    }
    println("Execution time: ${time / 1000.0} seconds")
}
suspend fun printCompetitionDays() {
    delay(1000)
    println("30天")
}
suspend fun printCompetitionCategory() {
    delay(1000)
    println("行動開發")
}
iThome鐵人賽
30天
行動開發
執行時間: 2.089 秒
Kotlin 中的協同程式遵循稱為結構化並行的重要概念
啟動一個新的協同程式
使用並行方式執行任務,請在程式碼中新增多個 launch() 函式,以便同時處理多個協同程式。
import kotlinx.coroutines.*
fun main() {
    val time = measureTimeMillis {
        runBlocking {
            println("iThome鐵人賽")
            launch {
                printCompetitionDays() 
            }
            launch {
                printCompetitionCategory() 
            }
        }
    println("Execution time: ${time / 1000.0} seconds")    
    }
}
suspend fun printCompetitionDays() {
    delay(1000)
    println("30天")
}
suspend fun printCompetitionCategory() {
    delay(1000)
    println("行動開發")
}
iThome鐵人賽
30天
行動開發
執行時間: 1.103 秒
執行時間: 2.089 秒  ->  執行時間: 1.103 秒
fun main() {
    val time = measureTimeMillis {
        runBlocking {
            println("iThome鐵人賽")
            launch {
                printCompetitionDays() 
            }
            launch {
                printCompetitionCategory() 
            }
            println("祝你堅持的三十天!")
        }
    println("Execution time: ${time / 1000.0} seconds")    
    }
}
....
iThome鐵人賽
祝你堅持的三十天!
30天
行動開發
執行時間: 1.098 秒
會發現為 printCompetitionDays() 和 printCompetitionCategory()  啟動兩個新的協同程式後,就可以繼續處理輸出 祝你堅持的三十天! 的下一個指示了。
用於啟動一個新的協同程式,並 返回一個 Deferred 物件。這個 Deferred 物件代表著這個協同程式的未來結果。我們可以在需要的時候,透過 await() 函式來取得這個結果。
相較於 launch(),async() 更常用於需要取得回傳值的非同步任務。
fun main() {
    runBlocking {
        println("iThome鐵人賽")
        val days: Deferred<String> = async {
            	getCompetitionDays()
        	}
        val category: Deferred<String> = async {
            	getCompetitionCategory()
        }
        println("${days.await()} ${category.await()}")
        println("祝你堅持的三十天!")
    }
}
suspend fun getCompetitionDays(): String {
    delay(1000)
    return "30天"
}
suspend fun getCompetitionCategory(): String {
    delay(1000)
    return "行動開發"
}
iThome鐵人賽
30天 行動開發
祝你堅持的三十天!
平行分解 (Parallel Decomposition) 則是將一個任務拆分成多個子任務,並同時執行這些子任務,以提高效率。在 Kotlin 協同程式中,我們可以利用 async 函式和 CoroutineScope 來輕鬆實現平行分解。
fun main() {
    runBlocking {
        println("iThome鐵人賽")
        println(getCompetitionReport())
        println("祝你堅持的三十天!")
    }
}
suspend fun getCompetitionReport() = coroutineScope {
    val category = async { getCompetitionCategory() }
    val days = async { getCompetitionDays() }
    
    
    "${category.await()} ${days.await()}"
}
suspend fun getCompetitionDays(): String {
    delay(1000)
    return "30天"
}
suspend fun getCompetitionCategory(): String {
    delay(1000)
    return "行動開發"
}
iThome鐵人賽
行動開發 30天
祝你堅持的三十天!
如果您知道程式碼的某些部分可能會擲回例外狀況,可以使用 try-catch 區塊包住該程式碼。
有Exception
iThome鐵人賽
Exception in thread "main" java.lang.AssertionError: 忘記寫文章斷賽了
 at FileKt.getCompetitionDays (File.kt:61) 
 at FileKt$getCompetitionDays$1.invokeSuspend (File.kt:-1) 
 at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith (ContinuationImpl.kt:33) 
解決
fun main() {
    runBlocking {
        println("iThome鐵人賽")
        println(getCompetitionReport())
        println("祝你堅持的三十天!")
    }
}
suspend fun getCompetitionReport() = coroutineScope {
    val category = async { getCompetitionCategory() }
    val days = async { 
        try {
        	getCompetitionDays()
        } catch (e: AssertionError) {
            println("斷賽原因 exception $e")
            "{ 沒po文 斷賽 }"
        }    
    }
    
    "${category.await()} ${days.await()}"
}
suspend fun getCompetitionDays(): String {
    delay(500)
    throw AssertionError("忘記寫文章斷賽了")
    return "30天"
}
suspend fun getCompetitionCategory(): String {
    delay(1000)
    return "行動開發"
}
iThome鐵人賽
斷賽原因 exception java.lang.AssertionError: 忘記寫文章斷賽了
行動開發 { 沒po文 斷賽 }
祝你堅持的三十天!
fun main() {
    runBlocking {
        println("iThome鐵人賽")
        println(getCompetitionReport())
        println("祝你堅持的三十天!")
    }
}
suspend fun getCompetitionReport() = coroutineScope {
    val category = async { getCompetitionCategory() }
    val days = async { getCompetitionDays() }
    
    delay(200)
    days.cancel() //決定棄賽
    
    "${category.await()} }"
}
suspend fun getCompetitionDays(): String {
    delay(500)
    return "30天"
}
suspend fun getCompetitionCategory(): String {
    delay(1000)
    return "行動開發"
}
iThome鐵人賽
行動開發
祝你堅持的三十天!
代表一個協同程式
可以用來控制協同程式的生命週期,例如啟動、取消、等待完成等。
val job = launch { ... }
job.cancel()

父項/子項關係會為子項、父項以及屬於同一父項的其他子項指定特定行為,因此十分重要。在前面的例子中,我們已透過天氣程式介紹這個行為。
CoroutineContext 可以被視為協同程式的上下文環境,它包含了協同程式執行所需要的一些資訊,例如:
import kotlinx.coroutines.*
fun main() = runBlocking {
    val scope = CoroutineScope(Dispatchers.Default)
    val job = scope.launch {
        try {
            delay(500L)
            println("Task started")
            delay(1500L)
            println("Task finished")
        } catch (e: CancellationException) {
            println("Task was cancelled")
        } finally {
            println("Cleaning up")
        }
    }
    delay(1300L)
    job.cancelAndJoin()
    println("Main program finished")
}
Task started
Task was cancelled
Cleaning up
Main program finished
import kotlinx.coroutines.*
fun main() {
    runBlocking {
        println("${Thread.currentThread().name} - runBlocking function")
                launch {
            println("${Thread.currentThread().name} - launch function")
            withContext(Dispatchers.Default) {
                println("${Thread.currentThread().name} - withContext function")
                delay(1000)
                println("10 results found.")
            }
            println("${Thread.currentThread().name} - end of launch function")
        }
        println("Loading...")
    }
}
main @coroutine#1 - runBlocking function
Loading...
main @coroutine#2 - launch function
DefaultDispatcher-worker-1 @coroutine#2 - withContext function
10 results found.
main @coroutine#2 - end of launch function